package edu.umd.ion.wavemeter.client;

import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.widgetideas.graphics.client.Color;
import com.google.gwt.widgetideas.graphics.client.GWTCanvas;
import com.google.gwt.xml.client.Element;
import com.google.gwt.xml.client.NodeList;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;

import edu.umd.ion.wavemeter.service.PatternData;
import edu.umd.ion.wavemeter.service.PatternRequest;
import edu.umd.ion.wavemeter.service.WavemeterData;
import edu.umd.ion.wavemeter.service.WavemeterDataMask;
import edu.umd.ion.wavemeter.service.WavemeterParameters;

/** 
 * Widget to display the Interferometer Patterns from the Wavemeter.
 * The widget can display one or more channels of the same Signal.
 * @author pmaunz
 *
 */
public class WavemeterPatternWidget extends Composite 
	implements WavemeterWidgetInterface {
	
	/**
	 *  Canvas width
	 */
	private int width=100;
	
	/**
	 *  Canvas Height
	 */
	private int height=200;
	
	/**
	 *  Maximum value of the pattern
	 */
	private int maxHeight=4096;
	
	private int NumChannels = -1;
	
	/**
	 *  Display Colors of the Channels
	 */
	private Color[] ChannelColors = { Color.BLUE, Color.RED, Color.GREEN, Color.BLACK,
									new Color(0x4b,0x00,0x82), new Color(0xa5,0x2a,0x2a), 
									new Color(0xff,0x00,0xff), new Color(0x77,0x88,0x99),
									Color.BLUE, Color.RED, Color.GREEN, Color.BLACK,
									new Color(0x4b,0x00,0x82), new Color(0xa5,0x2a,0x2a), 
									new Color(0xff,0x00,0xff), new Color(0x77,0x88,0x99) };
	
	/**
	 *  Our cache of the pattern data. Only from the ccd-array we care about.
	 */
	private PatternData[] PatternCache;
	
	/**
	 *  The index of the ccd-array we want to display.
	 */
	int PatternIndex;
	
	/**
	 *  Enable the request and display of patterns
	 */
	private CheckBox EnableCheck = new CheckBox("Enable");
	
	/**
	 *  ListBox for the choice of ccd-array.
	 */
	private ListBox PatternListBox = new ListBox();
	
	/**
	 *  The Canvas to display the patterns
	 */
	private GWTCanvas canvas;
	
	/**
	 *  Holds the Canvas, good to figure out the width.
	 */
	private HorizontalPanel CanvasPanel;
	
	/**
	 *  Panel to hold all controls
	 */
	private HorizontalPanel ControlPanel;
	
	/**
	 *  This Panel is the outer container.
	 */
	private VerticalPanel OuterPanel;
	
	/**
	 *  Delay between requests in s.
	 */
	private TextBox DelayTextBox = new TextBox();
	
	/**
	 *  The controls to enable the different channels.
	 */
	private ToggleButton[] ChannelChoice;
	
	/**
	 * @return the widget to be inserted in the Table of widgets
	 */
	public Widget getWidget()
	{
		return this;
	}
	
	/**
	 * resize the Canvas
	 * @param newwidth new canvas width
	 * @param newheight new canvas height
	 */
	public void resize(int newwidth, int newheight)
	{
		canvas.resize(newwidth, newheight);
		width = newwidth;
		height = newheight;
	}
	
	/**
	 *  used to set new patterns received from the server
	 *  @param data the data received from the server
	 *  @param NewRequestMask the requests for patterns to be used
	 *  in the next poll from the server 
	 */
	public void set(WavemeterData data, WavemeterDataMask NewRequestMask)
	{
		PatternIndex = Integer.parseInt(PatternListBox.getValue(PatternListBox.getSelectedIndex()));
		boolean updated = false;
		if (data.PatternList!=null) {
			for(int i=0; i<data.PatternList.size();++i )
			{
				PatternData CurrentPattern = data.PatternList.get(i);
				if (CurrentPattern.Index == PatternIndex )
				{
					PatternCache[CurrentPattern.Channel] = CurrentPattern;
					GWT.log("received pattern for channel "+CurrentPattern.Channel,null);
					updated = true;
				}
			}
		}
		if (EnableCheck.getValue())
		{
			for( int i=0; i<NumChannels; ++i)
			{
				if (ChannelChoice[i].isDown())
				{
					PatternRequest NewRequest = new PatternRequest();
					NewRequest.Channel = i;
					NewRequest.Delay = PatternCache[i]==null?0:Math.max(Integer.parseInt(DelayTextBox.getText())*1000,100);
					NewRequest.Pattern = PatternIndex;
					NewRequest.LastTimestamp = PatternCache[i]==null?0:PatternCache[i].Timestamp;
					NewRequestMask.addPatternRequest(NewRequest);					
				}
			}
		}
		if (updated)
			draw();
	}

	/**
	 * Make the Canvas Part of this widget visible or invisible
	 * @param visible true to show
	 */
	public void setCanvasVisible( boolean visible )
	{
		if (visible)
			autoresize();
		CanvasPanel.setVisible(visible);
	}
	
	/**
	 *  resize the canvas to fill its HorizontalPanel.
	 */
	public void autoresize()
	{
		int newwidth = Math.max(CanvasPanel.getOffsetWidth(),100);
		if (newwidth!=width)
		{
			width = newwidth;
			canvas.resize(width, height);
		}		
	}
	
	/**
	 * Draw the pattern for a single channel. 
	 * @param Pattern the pattern data
	 */
	private void drawChannel(PatternData Pattern)
	{
		GWT.log("Channel: "+Pattern.Channel+" Length: "+Pattern.Length+" ElementSize: "+Pattern.ElementSize,null);
		float xscale = width/(float) Pattern.Length;
		float yscale = height/(float) maxHeight;
		canvas.beginPath();
		canvas.setLineWidth(1);
		canvas.setStrokeStyle(ChannelColors[Pattern.Channel]);
		if (Pattern.ElementSize==2 && Pattern.ShortPattern!=null)
		{
			canvas.moveTo(0, height-Pattern.ShortPattern[0]*yscale);
			for (int j=1; j<Pattern.ShortPattern.length; ++j)
			{
				canvas.lineTo(j*xscale,height-Pattern.ShortPattern[j]*yscale);
			}
		}	
		else if (Pattern.ElementSize==4 && Pattern.IntPattern!=null)
		{
			canvas.moveTo(0, height-Pattern.IntPattern[0]*yscale);
			for (int j=0; j<Pattern.IntPattern.length; ++j)
			{
				canvas.lineTo(j*xscale,height-Pattern.IntPattern[j]*yscale);
			}
		}
		else
		{
			GWT.log("Pattern was null",null);
		}
		canvas.stroke();
	}
	
	/**
	 *  draw all currently cached and displayed channels
	 */
	public void draw()
	{
		try {
			canvas.clear();
			autoresize();
				for(int i=0; i<NumChannels; ++i )
				{
					if (PatternCache[i]!=null && ChannelChoice[i].isDown())
					{
						GWT.log("about to draw channel "+i,null);
						drawChannel(PatternCache[i]);
					}
				}
		}
		catch(Exception ex)
		{
		}
	}

	/**
	 *  Initialize most stuff. To be able to set properties from
	 *  the Settings XML initialize() has to be called afterwards.
	 */
	public WavemeterPatternWidget(int NumDisplayChannels)
	{
		NumChannels = NumDisplayChannels;
		PatternCache = new PatternData[NumChannels];
		ChannelChoice = new ToggleButton[NumChannels];
		CanvasPanel = new HorizontalPanel();
		CanvasPanel.setWidth("100%");
		PatternListBox.addItem("Interferometers",Integer.toString(WavemeterParameters.cSignal1Interferometers));
		PatternListBox.addItem("Wide Interferometer",Integer.toString(WavemeterParameters.cSignal1WideInterferometer));
		ControlPanel = new HorizontalPanel();
		ControlPanel.add(EnableCheck);
		ControlPanel.add(PatternListBox);
		for (int i=0; i<NumChannels; ++i)
		{
			ToggleButton ThisButton = new ToggleButton(Integer.toString(i));
			ThisButton.addStyleName("Channel-Select-Button");
			ThisButton.setStylePrimaryName("Channel-Select-Button-"+Integer.toString(i));
			ControlPanel.add(ThisButton);
			ChannelChoice[i] = ThisButton;
		}

		DelayTextBox.setText("10");
		DelayTextBox.setWidth("50px");
		ControlPanel.add(DelayTextBox);
		OuterPanel = new VerticalPanel();
		OuterPanel.add(ControlPanel);
		OuterPanel.add(CanvasPanel);
		 
		CanvasPanel.setVisible(false);
		EnableCheck.addClickHandler(new ClickHandler() {
	      	public void onClick(ClickEvent e) {
	      		setCanvasVisible(EnableCheck.getValue());
	      }
	    });
	}
	
	/** 
	 *  Initialization to parameters given in the DOM Element Node
	 */
	public void initialize(Element Node)
	{
		NodeList Styles = Node.getElementsByTagName("style");
		if (Styles.getLength()>0)
		{
			Element Style = (Element) Styles.item(0);
			width = Integer.parseInt(Style.getAttribute("width"));
			height = Integer.parseInt(Style.getAttribute("height"));
			PatternIndex = Style.hasAttribute("index")?Integer.parseInt(Style.getAttribute("index")):0;
			PatternListBox.setItemSelected(PatternIndex, true);
		}
		this.canvas = new GWTCanvas(width,height);
		CanvasPanel.add(canvas);
		OuterPanel.setWidth("100%");
	    initWidget(OuterPanel);	    
	    this.setStyleName("patterncanvas");
	}
}
 